<?php

require_once 'OAuth.php';

require_once 'HTTP/Request.php';

require_once 'OAuth/Http/Utility.php';

require_once 'OAuth/Config/Interface.php';

class OAuth_Client extends HTTP_Request implements OAuth_Config_Interface
{

    protected $_token = null;

    protected $_signatureMethod = 'HMAC-SHA1';

    protected $_requestScheme = OAuth::REQUEST_SCHEME_HEADER;

    protected $_version = '1.0';

    protected $_localUrl = null;

    protected $_requestTokenUrl = null;

    protected $_accessTokenUrl = null;

    protected $_userAuthorisationUrl = null;

    protected $_consumerKey = null;

    protected $_consumerSecret = null;

    protected $_rsaPrivateKey = null;

    protected $_excludeCustomParamsFromHeader = true;

    protected $_requestMethod = 'POST';

    public function __construct(array $oauthOptions, $uri = null, $config = null, $excludeCustomParamsFromHeader = true)
    {
        parent::__construct($uri, $config);
        $this->setOptions($oauthOptions);
        $this->_excludeCustomParamsFromHeader = (bool) $excludeCustomParamsFromHeader;
    }

    public function setMethod($method = OAuth::GET)
    {
        if ($method == OAuth::GET) {
            $this->_requestMethod = 'GET';
        } elseif($method == OAuth::POST) {
            $this->_requestMethod = 'POST';
        }
        return parent::setMethod($method);
    }

    public function sendRequest($saveBody = true)
    {
        $querystring = $this->_url->getQueryString();
        $paramsGet = $this->_parseQueryString($querystring);
        if ($this->_requestScheme == OAuth::REQUEST_SCHEME_HEADER) {
            $params = array();
            if (!empty($paramsGet)) {
                $params = array_merge($params, $paramsGet);
                $query = $this->getToken()->toQueryString(
                    $this->getUrl(), $this, $params
                );
                $this->addRawQueryString('');
            }
            if (!empty($this->_postData)) {
                $params = array_merge($params, $this->_postData);
                $query = $this->getToken()->toQueryString(
                    $this->getUrl(), $this, $params
                );
                $this->_postData = array();
            }
            $oauthHeaderValue = $this->getToken()->toHeader(
                $this->getUrl(), $this, $params
            );
            $this->addHeader('Authorization', $oauthHeaderValue);
            // handle non-OAuth protocol parameter passage
            if ($this->_requestMethod == OAuth::GET) {
                $this->addRawQueryString($query);
            } elseif($this->_requestMethod == OAuth::POST) {
                $this->addRawPostData($query); // deprec
            }
        } elseif ($this->_requestScheme == OAuth::REQUEST_SCHEME_POSTBODY) {
            if ($this->_requestMethod == OAuth::GET) {
                require_once 'OAuth/Exception.php';
                throw new OAuth_Exception('The client is configured to pass OAuth parameters through a POST body but request method is set to GET');
            }
            $raw = $this->getToken()->toQueryString(
                $this->getUrl(), $this, $this->_postData
            );
            $this->addRawPostData($raw); // deprec
            $this->_postData = array();
        } elseif ($this->_requestScheme == OAuth::REQUEST_SCHEME_QUERYSTRING) {
            $this->addRawQueryString('');
            $query = $this->getToken()->toQueryString(
                $this->getUrl(), $this, $paramsGet
            );
            $this->addRawQueryString($query);
        }
        return parent::sendRequest($saveBody);
    }

    public function setOptions(array $options)
    {
        foreach ($options as $key=>$value) {
            switch ($key) {
                case 'consumerKey':
                    $this->setConsumerKey($value);
                    break;
                case 'consumerSecret':
                    $this->setConsumerSecret($value);
                    break;
                case 'accessToken':
                    $this->setToken($value);
                    break;
                case 'signatureMethod':
                    $this->setSignatureMethod($value);
                    break;
                case 'version':
                    $this->setVersion($value);
                    break;
                case 'localUrl':
                    $this->setLocalUrl($value);
                    break;
                case 'requestTokenUrl':
                    $this->setRequestTokenUrl($value);
                    break;
                case 'accessTokenUrl':
                    $this->setAccessTokenUrl($value);
                    break;
                case 'userAuthorisationUrl':
                    $this->setUserAuthorisationUrl($value);
                    break;
            }
        }
        if (isset($options['requestScheme'])) {
            $this->setRequestScheme($options['requestScheme']);
        }
    }

    public function setToken(OAuth_Token_Access $token)
    {
        $this->_token = $token;
    }

    public function getToken()
    {
        return $this->_token;
    }

    public function setConsumerKey($key)
    {
        $this->_consumerKey = $key;
    }

    public function getConsumerKey()
    {
        return $this->_consumerKey;
    }

    public function setConsumerSecret($secret)
    {
        $this->_consumerSecret = $secret;
    }

    public function getConsumerSecret()
    {
        return $this->_consumerSecret;
    }

    public function setSignatureMethod($method)
    {
        $method = strtoupper($method);
        // this is a temporary restriction
        if (!in_array($method, array('HMAC-SHA1', 'RSA-SHA1', 'PLAINTEXT'))) {
            require_once 'OAuth/Exception.php';
            throw new OAuth_Exception(
                $method . ' is an unsupported signature method'
            );
        }
        $this->_signatureMethod = $method;
    }

    public function getSignatureMethod()
    {
        return $this->_signatureMethod;
    }

    public function setRequestScheme($scheme)
    {
        $scheme = strtolower($scheme);
        if (!in_array($scheme, array(
                OAuth::REQUEST_SCHEME_HEADER,
                OAuth::REQUEST_SCHEME_POSTBODY,
                OAuth::REQUEST_SCHEME_QUERYSTRING
            ))) {
            require_once 'OAuth/Exception.php';
            throw new OAuth_Exception(
                '\'' . $scheme . '\' is an unsupported request scheme'
            );
        }
        $this->_requestScheme = $scheme;
    }

    public function getRequestScheme()
    {
        return $this->_requestScheme;
    }

    public function getRequestMethod()
    {
        return $this->_requestMethod;
    }

    public function setVersion($version)
    {
        $this->_version = $version;
    }

    public function getVersion()
    {
        return $this->_version;
    }

    public function setLocalUrl($url)
    {
        if (!OAuth::checkUrl($url)) {
            require_once 'OAuth/Exception.php';
            throw new OAuth_Exception(
                '\'' . $url . '\' is not a valid URI'
            );
        }
        $this->_localUrl = $url;
    }

    public function getLocalUrl()
    {
        return $this->_localUrl;
    }

    public function setRequestTokenUrl($url)
    {
        if (!OAuth::checkUrl($url)) {
            require_once 'OAuth/Exception.php';
            throw new OAuth_Exception(
                '\'' . $url . '\' is not a valid URI'
            );
        }
        $this->_requestTokenUrl = $url;
    }

    public function getRequestTokenUrl()
    {
        return $this->_requestTokenUrl;
    }

    public function setAccessTokenUrl($url)
    {
        if (!OAuth::checkUrl($url)) {
            require_once 'OAuth/Exception.php';
            throw new OAuth_Exception(
                '\'' . $url . '\' is not a valid URI'
            );
        }
        $this->_accessTokenUrl = $url;
    }

    public function getAccessTokenUrl()
    {
        return $this->_accessTokenUrl;
    }

    public function setUserAuthorisationUrl($url)
    {
        if (!OAuth::checkUrl($url)) {
            require_once 'OAuth/Exception.php';
            throw new OAuth_Exception(
                '\'' . $url . '\' is not a valid URI'
            );
        }
        $this->_userAuthorisationUrl = $url;
    }

    public function getUserAuthorisationUrl()
    {
        return $this->_userAuthorisationUrl;
    }

    public function setRsaPrivateKey(Crypt_RSA_Key $key)
    {
        $this->_rsaPrivateKey = $key;
    }

    public function getRsaPrivateKey()
    {
        return $this->_rsaPrivateKey;
    }

    protected function _parseQueryString($querystring)
    {
        if (empty($querystring)) {
            return array();
        }
        $parts  = preg_split('/[\&]/', $querystring, -1, PREG_SPLIT_NO_EMPTY);
        $return = array();
        foreach ($parts as $part) {
            if (strpos($part, '=') !== false) {
                $value = substr($part, strpos($part, '=') + 1);
                $key   = substr($part, 0, strpos($part, '='));
            } else {
                $value = null;
                $key   = $part;
            }
            $key = rawurldecode($key);
            if (preg_match('#^(.*)\[([0-9a-z_-]*)\]#i', $key, $matches)) {
                $key = $matches[1];
                $idx = $matches[2];
                if (empty($return[$key]) || !is_array($return[$key])) {
                    $return[$key] = array();
                }
                if ($idx === '') {
                    $return[$key][] = $value;
                } else {
                    $return[$key][$idx] = $value;
                }
            } elseif (!empty($return[$key])) {
                $return[$key]   = (array)$return[$key];
                $return[$key][] = $value;
            } else {
                $return[$key] = $value;
            }
        }
        return $return;
    }

}